package com.xcity.game.equip;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xcity.db.entity.Employee;
import com.xcity.db.entity.Player;
import com.xcity.game.combat.attr.CombatAttribute;
import com.xcity.game.combat.attr.CombatAttributeType;
import com.xcity.game.common.Cause;
import com.xcity.game.common.GameStaticConfig;
import com.xcity.game.common.MessageConst;
import com.xcity.game.equip.Equip.State;
import com.xcity.game.equip.template.EquipAttrNumWeightTemplate;
import com.xcity.game.equip.template.EquipAttrWeightTemplate;
import com.xcity.game.equip.template.EquipBaseTemplate;
import com.xcity.game.equip.template.EquipDataTemplate;
import com.xcity.game.equip.template.EquipDataTemplate.Weight;
import com.xcity.game.equip.template.EquipSuitEffectTemplate;
import com.xcity.game.event.GameEvents;
import com.xcity.pomelo.PomeloRoute;
import com.xcity.pomelo.request.EquipmentBagRequest;
import com.xcity.pomelo.request.EquipmentLevelUpRequest;
import com.xcity.pomelo.request.EquipmentOperationRequest;
import com.xcity.pomelo.request.EquipmentSuitRequest;
import com.xcity.pomelo.response.EquipmentBagResponse;
import com.xcity.pomelo.response.EquipmentBagResponse.EquipmentSuit;
import com.xcity.pomelo.response.EquipmentLevelUpResponse;
import com.xcity.pomelo.response.EquipmentOperationResponse;
import com.xcity.pomelo.response.EquipmentSuitResponse;
import com.xcity.pomelo.response.HintResponse;

import naga.x.App;
import naga.x.game.template.TemplateException;
import naga.x.game.template.TemplateService;
import naga.x.net.Session;
import naga.x.net.annotation.OPHandler;
import naga.x.net.impl.pomelo.annotation.PomeloOP;
import naga.x.service.Service;
import naga.x.service.ServiceException;
import naga.x.util.CommonUtils;

@OPHandler
public class EquipmentService implements Service {

	public static final int STUFF_MAX_NUM = 50;
	protected static final Logger LOG = LoggerFactory.getLogger(EquipmentService.class);
	
	protected List<EquipBaseTemplate> baseTemplates;
	protected Map<EquipBrand, List<EquipBaseTemplate>> brand2baseTemplates;
	protected Map<EquipPosition, List<EquipBaseTemplate>> pos2baseTemplates;
	protected List<EquipDataTemplate> dataTemplates;
	protected List<EquipSuitEffectTemplate> suitTemplates;
	protected Map<EquipBrand, EquipmentSuit> suitsMap; // 缓存所有套装效果
//	protected List<EquipAttrWeightTemplate> attrWeightTemplates;
	protected List<EquipAttrNumWeightTemplate> attrNumWeightTemplates;
	
	@Override
	public void startup() throws ServiceException {
		init();
		validateDataTemplates();
	}
	
	private void validateDataTemplates() throws TemplateException {
		for (int i = 0; i < dataTemplates.size(); i++) {
			validateDataTemplate(dataTemplates.get(i));
		}
	}
	
	private void validateDataTemplate(EquipDataTemplate template) throws TemplateException {
		EquipAttrWeightTemplate produce = getAttrWeightTemplate(template.getQuality(), template.getPos());
		List<Weight> list = new ArrayList<Weight>();
		CombatAttributeType[] types = CombatAttributeType.values();
		for (int i = 0; i < types.length; i++) {
			CombatAttributeType type = types[i];
			int w = produce.getWeight(type);
			if (w <= 0) {
				continue;
			}
//			if (type == null) {
//				throw new TemplateException("装备产出（属性）配置错误: " + produce.getPos() + " - " + produce.getQuality() + ", " + i);
//			}
			if (template.getValue(type) <= 0) {
				throw new TemplateException("装备数值配置错误： " + template.getId() + ", " + type + " = " + template.getValue(type));
			}
			Weight weight = new Weight();
			weight.type = type;
			weight.weight = w;
			list.add(weight);
		}
		template.setWeights(new ArrayList<Weight>(list));
	}
	
	private void init() {
		baseTemplates = App.getApp().getServiceManager().get(TemplateService.class).getTemplatesList(EquipBaseTemplate.class);
		dataTemplates = App.getApp().getServiceManager().get(TemplateService.class).getTemplatesList(EquipDataTemplate.class);
		suitTemplates = App.getApp().getServiceManager().get(TemplateService.class).getTemplatesList(EquipSuitEffectTemplate.class);
//		attrWeightTemplates = App.getApp().getServiceManager().get(TemplateService.class).getTemplatesList(EquipAttrWeightTemplate.class);
		attrNumWeightTemplates = App.getApp().getServiceManager().get(TemplateService.class).getTemplatesList(EquipAttrNumWeightTemplate.class);
		
		brand2baseTemplates = new EnumMap<>(EquipBrand.class);
		pos2baseTemplates = new EnumMap<>(EquipPosition.class);
		for (int i = 0; i < baseTemplates.size(); i++) {
			EquipBaseTemplate template = baseTemplates.get(i);
			List<EquipBaseTemplate> list = brand2baseTemplates.get(template.getBrand());
			if (list == null) {
				list = new ArrayList<>();
				brand2baseTemplates.put(template.getBrand(), list);
			}
			list.add(template);
			
			list = pos2baseTemplates.get(template.getPos());
			if (list == null) {
				list = new ArrayList<>();
				pos2baseTemplates.put(template.getPos(), list);
			}
			list.add(template);
		}
		
		suitsMap = new EnumMap<EquipBrand, EquipmentSuit>(EquipBrand.class);
		List<EquipSuitEffectTemplate> templates = suitTemplates;
		for (int i = 0; i < templates.size(); i++) {
			EquipSuitEffectTemplate template = templates.get(i);
			suitsMap.put(template.getBrand(), new EquipmentSuit(template));
		}
	}

	@Override
	public void shutdown() throws ServiceException {
	}

	@Override
	public String getId() {
		return getClass().getName();
	}
	
	public Equip drop(Player player, EquipQuality quality, EquipBrand brand, EquipPosition position, String cause) {
		// 根据品牌、部位确定基础模板
		List<EquipBaseTemplate> baseTemplates = null;
		EquipBaseTemplate baseTemplate = null;
		if (brand != null) {
			baseTemplates = brand2baseTemplates.get(brand);
		}
		if (position != null) {
			if (baseTemplates != null) {
				for (int i = baseTemplates.size() - 1; i >= 0; i--) {
					EquipBaseTemplate t = baseTemplates.get(i);
					if (t.getPos() == position) {
						baseTemplate = t;
						break;
					}
				}
			} else {
				baseTemplates = pos2baseTemplates.get(position);
			}
		}
		if (baseTemplate == null) {
			if (baseTemplates == null) {
				baseTemplates = this.baseTemplates;
			}
			int index = CommonUtils.randomInt(baseTemplates.size());
			baseTemplate = baseTemplates.get(index);
		}
		
		// 根据品质确定属性数量
		int attrNum = attrNumWeightTemplates.get(quality.ordinal()).getNum();
//		EquipAttrWeightTemplate attrTemp = attrWeightTemplates.get(baseTemplate.getPos().ordinal());
		// 根据品质、部位获取数据模板
		EquipDataTemplate dataTemplate = getDataTemplate(quality, baseTemplate.getPos());
		// 生成装备属性
		List<CombatAttribute> attrs = dataTemplate.generate(attrNum);
		Equip equip = new Equip(baseTemplate, dataTemplate, attrs);
		player.getEquips().addEquip(equip, cause);
		App.getApp().getEventManager().addEvent(GameEvents.EVENT_EQUIP_ADD, player, equip);
		return equip;
	}
	
	/**
	 * 根据装备品质、部位获取数据模板
	 * @param quality
	 * @param position
	 * @return
	 */
	public EquipDataTemplate getDataTemplate(EquipQuality quality, EquipPosition position) {
		List<EquipDataTemplate> templates = dataTemplates;
		for (int i = 0, size = templates.size(); i < size; i++) {
			EquipDataTemplate template = templates.get(i);
			if (template.getQuality() == quality && template.getPos() == position) {
				return template;
			}
		}
		return null;
	}
	
	private EquipAttrWeightTemplate getAttrWeightTemplate(EquipQuality quality, EquipPosition position) {
		List<EquipAttrWeightTemplate> produces = App.getApp().getServiceManager().get(TemplateService.class).getTemplatesList(EquipAttrWeightTemplate.class);
		for (int i = 0; i < produces.size(); i++) {
			EquipAttrWeightTemplate t = produces.get(i);
			if (t.getPos() == position && t.getQuality() == quality) {
				return t;
			}
		}
		return null;
	}
	
	@PomeloOP(PomeloRoute.EQUIPMENTS)
	protected void requestEquipmentBag(Session session, EquipmentBagRequest req) {
		Player player = session.getClient();
		session.send(new EquipmentBagResponse(req.getSerial(), player));
	}
	
	@PomeloOP(PomeloRoute.EQUIPMENT_SUIT)
	protected void requestEquipmentSuit(Session session, EquipmentSuitRequest req) {
		if (req.getBrand() == null) {
			session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
			return;
		}
		EquipmentSuit suit = suitsMap.get(req.getBrand());
		session.send(new EquipmentSuitResponse(req.getSerial(), suit));
	}
	
	@PomeloOP(PomeloRoute.EQUIPMENT_LEVEL_UP)
	protected void requestLevelUp(Session session, EquipmentLevelUpRequest req) {
		long[] stuffIds = req.getStuffIds();
		if (stuffIds == null || stuffIds.length == 0) {
			session.send(new HintResponse(req.getSerial(), MessageConst.EQUIP_LEVEL_UP_STUFF_EMPTY));
			return;
		}
		Player player = session.getClient();
		if (player.getLevel() < GameStaticConfig.equipEnhanceOpenLvl) {
			session.send(new HintResponse(req.getSerial(), MessageFormat.format(MessageConst.EQUIP_STRENGTHEN_LEVEL_LIMIT, GameStaticConfig.equipEnhanceOpenLvl)));
			return;
		}
		Equip equip = player.getEquips().getEquip(req.getId());
		if (equip == null) {
			session.send(new HintResponse(req.getSerial(), MessageConst.EQUIP_NOT_FOUND));
			return;
		}
		if (equip.getLevel() >= EquipUtils.MAX_LEVEL) {
			session.send(new HintResponse(req.getSerial(), MessageConst.EQUIP_LEVEL_HIGHEST));
			return;
		}
		int oldLvl = equip.getLevel();
		List<Equip> stuffs = null;
		for (int i = 0, len = stuffIds.length; i < len; i++) {
			long stuffId = stuffIds[i];
			if (stuffId != equip.getId()) {
				Equip e = player.getEquips().getEquip(stuffId);
				if (e != null && e.getState() == State.IN_BAG) {
					if (stuffs == null)
						stuffs = new ArrayList<Equip>(STUFF_MAX_NUM);
					stuffs.add(e);
					if (stuffs.size() >= STUFF_MAX_NUM)
						break;
				}
			}
		}
		if (stuffs == null) {
			session.send(new HintResponse(req.getSerial(), MessageConst.EQUIP_LEVEL_UP_STUFF_EMPTY));
			return;
		}
		List<Long> successes = null;
		for (int i = 0; i < stuffs.size(); i++) {
			Equip stuff = stuffs.get(i);
			if (!player.costGold(stuff.getDataTemplate().getQuality().levelUpCost, Cause.EQUIPMENT_LEVEL_UP)) {
				if (successes == null) {
					session.send(new HintResponse(req.getSerial(), MessageConst.GOLD_NOT_ENOUGH));
					return;
				}
				break;
			}
			int exp = stuff.getDataTemplate().getQuality().expDeltaPerLvl + stuff.getExp();
			boolean succ = equip.addExp(exp, Cause.EQUIPMENT_LEVEL_UP);
			if (succ) {
				player.getEquips().removeEquip(stuff, Cause.EQUIPMENT_LEVEL_UP);
				if (successes == null)
					successes = new ArrayList<Long>(stuffs.size());
				successes.add(stuff.getId());
				if (equip.getLevel() >= EquipUtils.MAX_LEVEL) {
					break;
				}
			}
		}
		session.send(new EquipmentLevelUpResponse(req.getSerial(), equip, successes));
		App.getApp().getEventManager().addEvent(GameEvents.EVENT_EQUIP_ENHANCE, player, equip, oldLvl);
	}
	
	@PomeloOP(PomeloRoute.EQUIPMENT_OPERATION)
	protected void requestEquip(Session session, EquipmentOperationRequest req) {
		EquipPosition pos = EquipPosition.forCode(req.getPos());
		if (pos == null) {
			session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
			return;
		}
		if (req.getEid() == 0) {
			session.send(new HintResponse(req.getSerial(), MessageConst.EMPLOYEE_NOT_EXIST));
			return;
		}
		Player player = session.getClient();
		Employee employee = player.getEmployees().get(req.getEid());
		if (employee == null) {
			session.send(new HintResponse(req.getSerial(), MessageConst.EMPLOYEE_NOT_EXIST));
			return;
		}
		if (req.getEquip() == 0) { // 卸装
			employee.unEquip(pos.ordinal());
			session.send(new EquipmentOperationResponse(req.getSerial(), employee));
			return;
		}
		// 穿装
		Equip equip = player.getEquips().getEquip(req.getEquip());
		if (equip == null) {
			session.send(new HintResponse(req.getSerial(), MessageConst.EQUIP_NOT_FOUND));
			return;
		}
		if (equip.getState() == State.IN_EMPLOYEE) {
			session.send(new HintResponse(req.getSerial(), MessageConst.EQUIP_ALREADY));
			return;
		}
		employee.equip(equip.getDataTemplate().getPos().ordinal(), equip);
		session.send(new EquipmentOperationResponse(req.getSerial(), employee));
		App.getApp().getEventManager().addEvent(GameEvents.EVENT_EQUIP_SUIT_ADD, player, employee);
	}

}
